﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Animation;
using System.Windows.Markup;
using System.Collections.Generic;
using System.Windows.Media.Imaging;
using System.Windows.Media;
using System.Windows.Shapes;

namespace Silvery.Scene
{
    public enum SceneState
    {
        None,Leaveing, Entering, DataCompleted, Loaded
    }
    public delegate void SceneStateChangeEventHandler(object sender, SceneState e);
    public class SceneBase : DynamicObject
    {
        public SceneBase()
        {
            SceneObjects = new List<BaseObject>();
            Application.Current.Host.Content.Resized+=new EventHandler(Content_Resized);
            this.Children.Add(ScenePreviewImage = new Image());
        }
        void Content_Resized(object sender, EventArgs e)
        {
            VisibleRect = new Rect(X, Y, Application.Current.Host.Content.ActualWidth, Application.Current.Host.Content.ActualHeight);
            if(hero!=null)
                LookAtPoint(hero.Position);
        }
        Image ScenePreviewImage;
        public BitmapImage ScenePreview { get { return ScenePreviewImage.Source as BitmapImage; } set { ScenePreviewImage.Source = value; } }
        public SceneState SceneState { get; private set; }
        protected List<BaseObject> SceneObjects { get; private set; }
        public int[,] SceneGridData { get; private set; }
        protected Panel ObjectLayout { get; set; }
        public void AddSceneObject(BaseObject obj)
        {
            if (ObjectLayout != null)
            {
                ObjectLayout.Children.Add(obj);
                obj.Z = (int)obj.Y;
            }
        }
        public void RemoveSceneObject(BaseObject obj)
        {
            if (ObjectLayout != null)
                ObjectLayout.Children.Remove(obj);
        }
        public Rect VisibleRect { get; private set; }
        public event SceneStateChangeEventHandler SceneStateChanged;
        protected virtual void SceneStateChange(SceneState state)
        {
            SceneState = state;
            if (SceneStateChanged != null)
                SceneStateChanged(this, state);
        }
        protected AStarPathFinder _AStarPathFinder;
        protected virtual void OnEnter(FrameworkElement sceneElement, bool async = true)
        {
            OnLeave();
            SceneStateChange(Scene.SceneState.Entering);
            this.Width = ScenePreviewImage.Width = sceneElement.Width;
            this.Height = ScenePreviewImage.Height = sceneElement.Height;
            sceneElement.Loaded += new RoutedEventHandler(sceneElement_Loaded);
            if (async == false)
            {
                this.Children.Add(sceneElement);
                sceneElement_LayoutUpdated(sceneElement, EventArgs.Empty);
            }
            else
            {
                sceneElement.LayoutUpdated += new EventHandler(sceneElement_LayoutUpdated);
                this.Children.Add(sceneElement);
            }
            

        }
        /// <summary>
        /// 场景元素
        /// </summary>
        protected FrameworkElement SceneElement { get { return this.Children[1] as FrameworkElement; } }
        /// <summary>
        /// 当进入的时候调用
        /// </summary>
        /// <param name="xamldata"></param>
        protected virtual void OnEnter(string xamldata)
        {            
            OnEnter((FrameworkElement)XamlReader.Load(xamldata));
        }
        /// <summary>
        /// 当离开时候调用
        /// </summary>
        protected virtual void OnLeave()
        {
            if (ObjectLayout == null)
                return;
            this.CacheMode = null;
            SceneStateChange(Scene.SceneState.Leaveing);
            foreach (var item in SceneObjects)
                ObjectLayout.Children.Remove(item);
            ObjectLayout.Children.Remove(hero as FrameworkElement);
            this.Children.Remove(SceneElement);
            _AStarPathFinder.Dispose(); 
            _AStarPathFinder = null;            
            SceneGridData = null;
        }

        void sceneElement_Loaded(object sender, RoutedEventArgs e)
        {
            var scene = sender as FrameworkElement;
            scene.Loaded -= sceneElement_Loaded;
            if (Hero != null && ObjectLayout!=null)
                if(ObjectLayout.Children.Contains(Hero as UIElement) == false)
                    ObjectLayout.Children.Add(Hero as UIElement);
            
            SceneStateChange(Scene.SceneState.Loaded);
        }
        void sceneElement_LayoutUpdated(object sender, EventArgs e)
        {
            var scene = SceneElement;
            scene.LayoutUpdated -= sceneElement_LayoutUpdated;
            ObjectLayout = scene.FindName("l3") as Panel;
            foreach (FrameworkElement item in ObjectLayout.Children)
            {
                var zindex = Canvas.GetTop(item) + (item.Height * item.RenderTransformOrigin.Y);
                Canvas.SetZIndex(item, (int)zindex);
            }
            WriteableBitmap ObstructWriteable = new WriteableBitmap(scene, null);
            var ObstructLayout = scene.FindName("l0") as Panel;
            if(ObstructLayout == null)
                throw new NotImplementedException("场景数据不正确或者不规范");
            ObstructLayout.Visibility = System.Windows.Visibility.Visible;
            int length_X = ObstructWriteable.PixelWidth / (int)GlobleConfig.SceneGridSize.Width;
            int length_Y = ObstructWriteable.PixelHeight / (int)GlobleConfig.SceneGridSize.Height;
            if (ObstructLayout.Children.Count != 0)
            {
                int lw = ObstructWriteable.PixelWidth;
                SceneGridData = new int[length_Y, length_X];
                _AStarPathFinder = new AStarPathFinder(length_X, length_Y);
                for (int i = 0; i < length_Y; i++)
                {
                    for (int j = 0; j < length_X; j++)
                    {
                        int data = ObstructWriteable.Pixels[i * (int)GlobleConfig.SceneGridSize.Height * lw + j * (int)GlobleConfig.SceneGridSize.Width];
                        SceneGridData[i, j] = data;
                        if (data == GlobleConfig.BlockColorInt)
                        {
                            _AStarPathFinder.Block(j, i);
                        }
                    }
                }
            }
            else
            {
                if (scene.Tag != null)
                    if (scene.Tag is int[,])
                        SceneGridData = (int[,])scene.Tag;
                    else
                        SceneGridData = new int[length_Y, length_X];
                else
                    SceneGridData = new int[length_Y, length_X];
                
                for (int i = 0; i < length_Y; i++)
                {
                    for (int j = 0; j < length_X; j++)
                    {
                        var data = SceneGridData[i, j];
                        if (data == -1)
                            _AStarPathFinder.Block(j, i);
                    }
                }
            }
            ObstructLayout.Children.Clear();
            ObstructLayout.Visibility = System.Windows.Visibility.Collapsed;
            scene.Tag = SceneGridData;
            if (hero != null && ObjectLayout!=null)
                if(ObjectLayout.Children.Contains(hero as FrameworkElement) == false)
                    ObjectLayout.Children.Add(hero as FrameworkElement);
            SceneStateChange(Scene.SceneState.DataCompleted);
            
            this.CacheMode = new BitmapCache();
            Storyboard ani_def = scene.FindName("ANI_DEF") as Storyboard;
            if (ani_def != null)
                ani_def.Begin();
        }
        /// <summary>
        /// 显示或隐蔽所有的不可达点
        /// </summary>
        /// <param name="isShow">true是显示,false是隐蔽</param>
        public void ShowAllBlocks(bool isShow = true)
        {
            if (isShow)
            {
                int length_X = (int)(Width / GlobleConfig.SceneGridSize.Width);
                int length_Y = (int)(Height / GlobleConfig.SceneGridSize.Height);
                for (int i = 0; i < length_Y; i++)
                {
                    for (int j = 0; j < length_X; j++)
                    {
                        if (SceneGridData[i, j] == GlobleConfig.BlockColorInt)
                        {
                            var rect = new Rectangle()
                            {
                                Stroke = new SolidColorBrush(Colors.Transparent),
                                Fill = new SolidColorBrush(Colors.Brown),
                                Width = GlobleConfig.SceneGridSize.Width,
                                Height = GlobleConfig.SceneGridSize.Height
                            };
                            ObjectLayout.Children.Add(rect);
                            Canvas.SetLeft(rect, j * GlobleConfig.SceneGridSize.Width - GlobleConfig.SceneGridSize.Width);
                            Canvas.SetTop(rect, i * GlobleConfig.SceneGridSize.Height - GlobleConfig.SceneGridSize.Height);

                        }
                    }
                }
            }
            else
            {
                for (int i = ObjectLayout.Children.Count - 1; i >= 0; i--)
                {
                    if (ObjectLayout.Children[i] is Rectangle)
                        ObjectLayout.Children.RemoveAt(i);
                }
            }
        }

        private ICanMove hero;
        /// <summary>
        /// 链接的英雄
        /// </summary>
        public ICanMove Hero
        {
            get { return hero; }
            set
            {
                if (hero == value)
                    return;
                
                if (ObjectLayout != null)
                {
                    if (ObjectLayout.Children.Contains(hero as UIElement))
                        ObjectLayout.Children.Remove(hero as UIElement);
                    ObjectLayout.Children.Add(value as UIElement);                    
                }
                if (hero != null)
                    hero.PositionChanged-=hero_PositionChanged;
                if(value !=null)
                    value.PositionChanged += new PositionEventHandler(hero_PositionChanged);

                hero = value;
            }
        }

        void hero_PositionChanged(object sender, DependencyPropertyChangedEventArgs e)
        {            
            LookAtPoint(Hero.Position);
        }
        public virtual void LookAtPoint(Point pos)
        {
            double vWidth = VisibleRect.Width;
            double vHeight = VisibleRect.Height;
            double x = (vWidth / 2) - pos.X;
            double y = (vHeight / 2) - pos.Y;

            if (X == x && Y == y)
                return;
            if (x > 0)
                X = 0;
            else
            {
                double temp = Width - vWidth;
                if (x + temp < 0)
                    X = -temp;
                else
                    X = x;
            }
            if (y > 0)
                Y = 0;
            else
            {
                double temp = Height - vHeight;
                if (y + temp < 0)
                    Y = -temp;
                else
                    Y = y;
            }
        }
    }
}
